# hex.py # usage: python hex.py yourUsername yourPassword opponentUsername # Programming by Eric Rollins # # Copyright (c) 2002 Eric Rollins # www.acm.org/~rollins # hex comes with Absolutely No Warranty. # This is free software, and you are welcome to # redistribute it under certain conditions # (for details see:GNU General Public License, # http://www.gnu.org/copyleft/gpl.html) # version 1.3 - 11x11 board import sys import jabber import Tkinter import threading # constants SIDE_NONE = 0 SIDE_RED = 1 SIDE_BLUE = 2 TRUE = 1 FALSE = 0 # edit here for board size ROWS = 11 COLS = 11 #============================================================ def oppositeSide(side): if side == SIDE_RED: return SIDE_BLUE elif side == SIDE_BLUE: return SIDE_RED return SIDE_NONE #============================================================ # View class Canvas: SCALE = 20 XOFF = 20 YOFF = 20 WIDTH = int(2.8*SCALE*COLS) HEIGHT = int(1.7*SCALE*ROWS) def do_release(self,event): x = event.x y = event.y if g_app.getSide() == SIDE_NONE or g_app.getWaiting(): return row = (y - self.YOFF) / (1.5*self.SCALE) if row < 0 or row > (ROWS): return row = int(row) col = (x - self.XOFF - row*0.86*self.SCALE) / (1.72*self.SCALE) if col < 0 or col > (COLS): return col = int(col) if g_app.getGame().getBoard().getCell(row,col) != SIDE_NONE: return g_app.getGame().setCell(row,col,g_app.getSide()) g_app.send_turn(row,col) #------------------------------------------------------------------------------------------------------------------------ def __init__(self,parent): self.canvas = Tkinter.Canvas(parent, width=self.WIDTH, height=self.HEIGHT) self.canvas.pack() self.canvas.bind("",self.do_release) self.pieces = [] self.drawPiece(3,-2,SIDE_BLUE,FALSE) self.drawPiece(3,(COLS+1),SIDE_BLUE,FALSE) #------------------------------------------------------------------------------------------------------------------------ def clear(self): for p in self.pieces: self.canvas.delete(p) #------------------------------------------------------------------------------------------------------------------------ def drawHex(self,row,col): x = self.XOFF + row*0.86*self.SCALE+col*1.72*self.SCALE y = self.YOFF + row*1.5*self.SCALE self.canvas.create_line( x, 0.5*self.SCALE + y, 0.86*self.SCALE + x, y, 1.72*self.SCALE + x, 0.5*self.SCALE + y, 1.72*self.SCALE + x, 1.5*self.SCALE + y, 0.86*self.SCALE + x, 2.0*self.SCALE + y, x, 1.5*self.SCALE + y, x, 0.5*self.SCALE + y, 0.86*self.SCALE + x, y) #------------------------------------------------------------------------------------------------------------------------ def drawPiece(self,row,col,side,inPieces = TRUE): x = self.XOFF + row*0.86*self.SCALE+col*1.72*self.SCALE y = self.YOFF + row*1.5*self.SCALE if side == SIDE_RED: fillColor = 'red' elif side == SIDE_BLUE: fillColor = 'blue' if side != SIDE_NONE: circle = self.canvas.create_oval( x+ self.SCALE/3.5, y + self.SCALE/3.5, x + 2.0*self.SCALE - self.SCALE/2.3, y + 2.0*self.SCALE - self.SCALE/2.3,fill=fillColor) if inPieces: self.pieces.append(circle) #============================================================ # Model class Board: def __init__(self): self.board = [] for row in range(ROWS): self.board.append([]) for col in range(COLS): self.board[row].append(SIDE_NONE) self.clear() #----------------------------------------------------------------------------------------------------------------------- def clear(self): for row in range(ROWS): for col in range(COLS): self.board[row][col] = SIDE_NONE #----------------------------------------------------------------------------------------------------------------------- def getCell(self,row,col): return self.board[row][col] #------------------------------------------------------------------------------------------------------------------------ def setCell(self,row,col,val): self.board[row][col] = val #============================================================ # controller for Canvas (View) + Board (Model) class Game: def __init__(self,parent): self.canvas = Canvas(parent) self.board = Board() #------------------------------------------------------------------------------------------------------------------------ def drawAll(self): for row in range(ROWS): for col in range(COLS): self.canvas.drawHex(row,col) self.canvas.drawPiece(row,col,self.board.getCell(row,col)) #------------------------------------------------------------------------------------------------------------------------ def setCell(self,row,col,side): cur = self.board.getCell(row,col) if cur == SIDE_RED or cur == SIDE_BLUE: print "ERROR: cell already set!!" sys.exit() self.board.setCell(row,col,side) self.canvas.drawPiece(row,col,side) #------------------------------------------------------------------------------------------------------------------------ def newGame(self): self.canvas.clear self.board.clear #------------------------------------------------------------------------------------------------------------------------ def getBoard(self): return self.board #============================================================ def msgHandler(conn,msg): print msg if msg.getError(): print "ERROR: network error" sys.exit() g_app.conn.readData = msg.getBody() #-------------------------------------------------------------------------------------------------------------------------- class Connection: def __init__(self,username,password,opponent): self.username = username self.password = password self.opponent = opponent + "@jabber.org" self.conn = jabber.Client('jabber.org',debug=TRUE,log=sys.stderr) #------------------------------------------------------------------------------------------------------------------------ def connect(self): self.conn.connect() self.conn.setMessageHandler(msgHandler) self.conn.auth(self.username,self.password,'hex') self.conn.send(jabber.Presence()) #------------------------------------------------------------------------------------------------------------------------ def disconnect(self): self.conn.disconnect() #------------------------------------------------------------------------------------------------------------------------ def send(self,message): msg = jabber.Message(self.opponent,message) msg.setType('chat') self.conn.send(msg) #------------------------------------------------------------------------------------------------------------------------ def receive(self): self.readData = None while self.readData == None: self.conn.process(1) return self.readData #============================================================ class SendCompletionThread(threading.Thread): def run(self): print "about to blocking recv" data = g_app.conn.receive() print "recvd: " + data g_app.setStatusText('opponent move ' + data + ' ; select move') splitData = data.split(',') row = int(splitData[0]) col = int(splitData[1]) if (row < 0) or (row > (ROWS-1)) or (col < 0) or (col > (COLS-1)): print "ERROR: recd row or col out of bounds" # BUG: only kills bkgnd thread on OSX sys.exit() g_app.game.setCell(row,col,oppositeSide(g_app.getSide())) g_app.setWaiting(FALSE) class App: WIDTH = 400 def recv_turn(self): self.setWaiting(TRUE) sendCompletionThread = SendCompletionThread() sendCompletionThread.start() #------------------------------------------------------------------------------------------------------------------------ def send_turn(self,row,col): msg = "%d,%d" % (row,col) print "about to send: " + msg self.conn.send(msg) self.setStatusText('Waiting for opponents move...') self.recv_turn() #------------------------------------------------------------------------------------------------------------------------ def start_blue(self): if self.getWaiting(): return self.setSide(SIDE_BLUE) self.game.newGame() self.rNewBtn.config(state='disabled') self.bNewBtn.config(state='disabled') self.setSideMsg('Side = Blue') self.setStatusText('Waiting for Red opponents first move...') self.recv_turn() #------------------------------------------------------------------------------------------------------------------------ def start_red(self): if self.getWaiting(): return self.setSide(SIDE_RED) self.game.newGame() self.rNewBtn.config(state='disabled') self.bNewBtn.config(state='disabled') self.setSideMsg('Side = Red') self.setStatusText('Select move') #------------------------------------------------------------------------------------------------------------------------ def setStatusText(self,msg): self.status.config(text=msg) self.status.config(width=self.WIDTH) self.status.pack() #------------------------------------------------------------------------------------------------------------------------ def getSide(self): return self.side def setSide(self,side): self.side = side #------------------------------------------------------------------------------------------------------------------------ def setSideMsg(self,msg): self.sideMsg.config(text=msg) self.sideMsg.config(width=self.WIDTH) self.sideMsg.pack() #------------------------------------------------------------------------------------------------------------------------ def getWaiting(self): return self.waiting def setWaiting(self,waiting): self.waiting = waiting #------------------------------------------------------------------------------------------------------------------------ def getGame(self): return self.game #------------------------------------------------------------------------------------------------------------------------ def __init__(self): self.side = SIDE_NONE self.waiting = FALSE #------------------------------------------------------------------------------------------------------------------------ def run(self): ph = { 'padx' : 10, 'pady' : 10 } # tk formatting if len(sys.argv) > 1: titleMsg = "Hex - " + sys.argv[1] else: titleMsg = "Hex - " root = Tkinter.Tk() top = Tkinter.Frame(root) # title=titleMsg) self.game = Game(top) self.game.drawAll() self.sideMsg = Tkinter.Message(top, text='Side = Unset', width=self.WIDTH) self.sideMsg.pack(ph) self.rNewBtn = Tkinter.Button(top, text='Start New Game As Red (moves first)',command=self.start_red) self.rNewBtn.pack(ph) self.bNewBtn = Tkinter.Button(top, text='Start New Game As Blue (moves second)',command=self.start_blue) self.bNewBtn.pack(ph) self.status = Tkinter.Message(top, text='Press ', width=self.WIDTH) self.status.pack(ph) top.pack() if len(sys.argv) != 4: self.setStatusText('ERROR: must specify on command line') self.rNewBtn.config(state='disabled') self.bNewBtn.config(state='disabled') self.conn = None else: self.conn = Connection(sys.argv[1],sys.argv[2],sys.argv[3]) self.conn.connect() try: root.mainloop() finally: if self.conn: self.conn.disconnect() print 'disconnected' #============================================================ g_app = App() g_app.run()